import {
  cadAppLog,
  CMEVENTS,
  DocumentUtils,
  FeatureManager,
  getDocOriginalKey,
  IAssemblyDocTreeNode,
  ShapeChangePendingHandler,
  Types,
} from '@amcax/base';
import {
  PickerAssembly,
  PickerMgr,
  Renderer3D,
  RendererMgr,
} from '@amcax/renderer';
import { measureRenderManager } from '@app-base/manager/MeasureRenderManager';
import { updatePendingAdjustCamera } from '@app-cad-env/product/commands/common/hooks/useDollyCamera';
import { Constants, PRODUCT_EVENT, WS_EVENTS } from '@app-cad/common/Constants';
import {
  collectDxidsFromObjects,
  handleCoreEvent,
  registerImportedFlagEvent,
  renderModel,
  renderThread,
} from '@app-cad/common/events/CoreEvent';
import { cmdMgr } from '@app-cad/manager/CmdManager';
import { referenceRenderManager } from '@app-cad/manager/ReferenceRenderManager';
import {
  CoreBasicCommands,
  coreEngine,
  ctx2,
  docMgr,
  userEvents,
} from '@app-cad/module';
import { shapeChangePendingHandler } from '@app-cad/pages/product-support/core-event';
import { allParts } from '@app-cad/pages/product-support/part-funcs';
import { Dispatch, SetStateAction } from 'react';
import {
  applyNodesToTree,
  buildAssemblyTree,
  checkAndFitView,
  gatherSubPartsOfRootChildren,
  rebuildConstraintsSubTree,
} from './product-support/assembly-funcs';

export interface DocInitFuncOptions {
  docTreeRoot: IAssemblyDocTreeNode & {
    documentInfo: CadDocument.DocumentInfo;
  };
  setDocTree: Dispatch<SetStateAction<IAssemblyDocTreeNode>>;
  setShowLoading: Dispatch<SetStateAction<boolean>>;
  setLoadingProgress: Dispatch<SetStateAction<number>>;
}

export type DocInitFunc = (options: DocInitFuncOptions) => Promise<void>;

export const visualDataErrorMessage = 'Failed to get visual data: ';

const readOnly = DocumentUtils.isReadOnly();

async function initCoreEngine(
  docTreeRoot: IAssemblyDocTreeNode,
  coreEventHandler?: ShapeChangePendingHandler,
) {
  const { key: documentId, version: documentVersion } = docTreeRoot;
  let engine = null;
  const startEngine = async () => {
    engine = await coreEngine.start(
      getDocOriginalKey(documentId),
      documentVersion,
    );
    if (coreEventHandler) {
      engine.registerShapeChangePendingHandler(coreEventHandler);
    }
    ctx2.set(Constants.CORE_ENGINE, engine);
  };
  await startEngine();

  const WsStatusChange = (data: boolean) => {
    if (!data) {
      const WsStatus = !data;
      userEvents.emit(WS_EVENTS.WS_LOSE_EVENT, WsStatus);
    }
  };
  userEvents.on(CMEVENTS.WS_STATUS_CHANGED, WsStatusChange);

  (window as any)?.electronCenter?.on(
    CMEVENTS.WS_STATUS_CHANGED,
    async (status: boolean) => {
      if (status) {
        await startEngine();
        userEvents.emit(WS_EVENTS.WS_LOSE_EVENT, false);
      }
    },
  );

  await CoreBasicCommands.openDocument(documentVersion, readOnly);

  // 设置全局物理材质
  ctx2.viewer.sceneSetup.setUpGlobalPBRMaterial();
}

export async function initPart({
  docTreeRoot,
  setDocTree,
  setShowLoading,
  setLoadingProgress,
}: DocInitFuncOptions) {
  await initCoreEngine(docTreeRoot, handleCoreEvent);
  registerImportedFlagEvent();

  await docMgr.updateDocumentObjects();
  await docMgr.updateDocumentMaterials();
  await docMgr.resetDocumentObject();

  const dxids: string[] = collectDxidsFromObjects(
    docMgr.documentObjects,
    false,
  );
  const totalCount: number = dxids.length;
  let processTimer: NodeJS.Timeout | null = null;
  let currentCount: number = 0;
  setLoadingProgress(0);

  if (totalCount > 50) {
    await renderModel(dxids, { needFitObjectView: true, force: false });
  } else {
    processTimer = setInterval(() => {
      if (currentCount < totalCount - 1) {
        setLoadingProgress((++currentCount / totalCount) * 100);
      }
    }, 100);
    const modelsData = await CoreBasicCommands.getVisualData().catch((e) => {
      throw new Error(visualDataErrorMessage + e);
    });
    cadAppLog('Models Visual Data', modelsData);

    await RendererMgr.getRender<Renderer3D>('3d').renderModels(modelsData);
  }

  // 螺纹渲染
  await renderThread();

  clearInterval(processTimer);
  setLoadingProgress(-1);
  setShowLoading(false);

  cmdMgr.updateUndoSize();

  await afterRender();

  setDocTree(docTreeRoot);
}

const afterRender = async () => {
  cadAppLog('[debug]afterRender...');
  // 渲染参考
  referenceRenderManager.renderBasePlanes();
  await referenceRenderManager.renderReferenceObjects();
  await measureRenderManager.rerenderMeasureResults();

  cadAppLog('[debug]fitObjectView', ctx2.viewer);
  setTimeout(() => {
    ctx2.viewer.fitObjectView(400);
    ctx2.viewer.sceneSetup.updateViewportSize();
  }, 600);
};

export async function initProduct({
  docTreeRoot,
  setDocTree,
  setShowLoading,
  setLoadingProgress,
}: DocInitFuncOptions) {
  if (!FeatureManager.isFeatureEnabled('assemble')) {
    setLoadingProgress(-1);
    setShowLoading(false);
    setDocTree(docTreeRoot);
  }

  await initCoreEngine(docTreeRoot, (event) => {
    shapeChangePendingHandler(event, docTreeRoot, setDocTree);
    cmdMgr.updateUndoSize();
  });

  docMgr.basePlanes.forEach((object) =>
    // we permanently set isRecursiveVisible to true
    // and when clicked, wo test the visibility of the object itself only
    ctx2.dataManager.addObjectsInfo({ ...object, isRecursiveVisible: true }),
  );

  docTreeRoot.children = [
    {
      key: 'ORIGIN',
      collectionUUID: '',
      version: 0,
      title: '原点',
      visible: true,
      type: Types.ORIGIN,
      parent: docTreeRoot,
    },
    {
      key: 'XOY',
      collectionUUID: '',
      version: 0,
      title: 'XY平面',
      visible: true,
      type: Types.BASIC_PLANE,
      parent: docTreeRoot,
    },
    {
      key: 'YOZ',
      collectionUUID: '',
      version: 0,
      title: 'YZ平面',
      visible: true,
      type: Types.BASIC_PLANE,
      parent: docTreeRoot,
    },
    {
      key: 'XOZ',
      collectionUUID: '',
      version: 0,
      title: 'XZ平面',
      visible: true,
      type: Types.BASIC_PLANE,
      parent: docTreeRoot,
    },
  ];

  (window as any)?.electronCenter?.emit(CMEVENTS.MODELS_RENDERER_START);
  setLoadingProgress(0);

  userEvents.on(PRODUCT_EVENT.ASSEMBLY_RENDERED, () => {
    updatePendingAdjustCamera(true);
    setTimeout(() => {
      checkAndFitView();
      setLoadingProgress(-1);
      setShowLoading(false);

      ctx2.viewer.sceneSetup.adjustCameraForVisualObjects();
      updatePendingAdjustCamera(false);

      measureRenderManager.rerenderMeasureResults();

      (window as any)?.electronCenter?.emit(CMEVENTS.MODELS_RENDERER_END);
    }, 1000);
  });

  userEvents.on(PRODUCT_EVENT.UPDATE_CONSTRAINTS, async () => {
    await cmdMgr.updateUndoSize();
    await rebuildConstraintsSubTree(docTreeRoot);

    applyNodesToTree(docTreeRoot);
  });

  await buildAssemblyTree(docTreeRoot, {
    initialRender: true,
    noRender: false,
  });

  const picker = PickerMgr.getPicker<PickerAssembly>('assembly');
  picker.allParts = allParts;
  picker.docTreeRoot = docTreeRoot;

  referenceRenderManager.renderBasePlanes();

  gatherSubPartsOfRootChildren(docTreeRoot);
  setDocTree(docTreeRoot);
}
